home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / share / hplip / base / mdns.py < prev    next >
Text File  |  2008-10-13  |  10KB  |  335 lines

  1. # -*- coding: utf-8 -*-
  2. #
  3. # (c) Copyright 2003-2007 Hewlett-Packard Development Company, L.P.
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  18. #
  19. # Author: Don Welch
  20. #
  21.  
  22. # RFC 1035
  23.  
  24. # Std Lib
  25. import sys
  26. import time
  27. import socket
  28. import select
  29. import struct
  30. import random
  31. import re
  32. import cStringIO
  33.  
  34. # Local
  35. from g import *
  36. import utils
  37.  
  38. MAX_ANSWERS_PER_PACKET = 24
  39.  
  40. QTYPE_A = 1
  41. QTYPE_TXT = 16
  42. QTYPE_SRV = 33
  43. QTYPE_AAAA = 28
  44. QTYPE_PTR = 12
  45.  
  46. QCLASS_IN = 1
  47.  
  48.  
  49. def read_utf8(offset, data, l):
  50.     return offset+l, data[offset:offset+l].decode('utf-8')
  51.  
  52. def read_data(offset, data, l):
  53.     return offset+l, data[offset:offset+l]
  54.  
  55. def read_data_unpack(offset, data, fmt):
  56.     l = struct.calcsize(fmt)
  57.     return offset+l, struct.unpack(fmt, data[offset:offset+l])
  58.  
  59. def read_name(offset, data):
  60.     result = ''
  61.     off = offset
  62.     next = -1
  63.     first = off
  64.  
  65.     while True:
  66.         l = ord(data[off])
  67.         off += 1
  68.  
  69.         if l == 0:
  70.             break
  71.  
  72.         t = l & 0xC0
  73.  
  74.         if t == 0x00:
  75.             off, utf8 = read_utf8(off, data, l)
  76.             result = ''.join([result, utf8, '.'])
  77.  
  78.         elif t == 0xC0:
  79.             if next < 0:
  80.                 next = off + 1
  81.  
  82.             off = ((l & 0x3F) << 8) | ord(data[off])
  83.  
  84.             if off >= first:
  85.                 log.error("Bad domain name (circular) at 0x%04x" % off)
  86.                 break
  87.  
  88.             first = off
  89.  
  90.         else:
  91.             log.error("Bad domain name at 0x%04x" % off)
  92.             break
  93.  
  94.     if next >= 0:
  95.         offset = next
  96.  
  97.     else:
  98.         offset = off
  99.  
  100.     return offset, result
  101.  
  102.  
  103. def write_name(packet, name):
  104.     for p in name.split('.'):
  105.         utf8_string = p.encode('utf-8')
  106.         packet.write(struct.pack('!B', len(utf8_string)))
  107.         packet.write(utf8_string)
  108.  
  109.  
  110. def create_outgoing_packets(answers):
  111.     index = 0
  112.     num_questions = 1
  113.     first_packet = True
  114.     packets = []
  115.     packet = cStringIO.StringIO()
  116.     answer_record = cStringIO.StringIO()
  117.  
  118.     while True:
  119.         packet.seek(0)
  120.         packet.truncate()
  121.  
  122.         num_answers = len(answers[index:index+MAX_ANSWERS_PER_PACKET])
  123.  
  124.         if num_answers == 0 and num_questions == 0:
  125.             break
  126.  
  127.         flags = 0x0200 # truncated
  128.         if len(answers) - index <= MAX_ANSWERS_PER_PACKET:
  129.             flags = 0x0000 # not truncated
  130.  
  131.         # ID/FLAGS/QDCOUNT/ANCOUNT/NSCOUNT/ARCOUNT
  132.         packet.write(struct.pack("!HHHHHH", 0x0000, flags, num_questions, num_answers, 0x0000, 0x0000))
  133.  
  134.         if num_questions:
  135.             # QNAME
  136.             write_name(packet, "_pdl-datastream._tcp.local") # QNAME
  137.             packet.write(struct.pack("!B", 0x00))
  138.  
  139.             # QTYPE/QCLASS
  140.             packet.write(struct.pack("!HH", QTYPE_PTR, QCLASS_IN)) 
  141.  
  142.         first_record = True
  143.         for d in answers[index:index+MAX_ANSWERS_PER_PACKET]:
  144.             answer_record.seek(0)
  145.             answer_record.truncate()
  146.  
  147.             # NAME
  148.             if not first_packet and first_record:
  149.                 first_record = False
  150.                 write_name(answer_record, "_pdl-datastream._tcp.local")
  151.                 answer_record.write(struct.pack("!B", 0x00))
  152.             else:
  153.                 answer_record.write(struct.pack("!H", 0xc00c)) # Pointer
  154.  
  155.             # TYPE/CLASS
  156.             answer_record.write(struct.pack("!HH", QTYPE_PTR, QCLASS_IN)) 
  157.  
  158.             # TTL
  159.             answer_record.write(struct.pack("!I", 0xffff))
  160.             rdlength_pos = answer_record.tell()
  161.  
  162.             # RDLENGTH
  163.             answer_record.write(struct.pack("!H", 0x0000)) # (adj later)
  164.  
  165.             # RDATA
  166.             write_name(answer_record, d)
  167.             answer_record.write(struct.pack("!H", 0xc00c)) # Ptr
  168.  
  169.             # RDLENGTH
  170.             rdlength = answer_record.tell() - rdlength_pos - 2
  171.             answer_record.seek(rdlength_pos)
  172.             answer_record.write(struct.pack("!H", rdlength))
  173.  
  174.             answer_record.seek(0)
  175.             packet.write(answer_record.read())
  176.  
  177.         packets.append(packet.getvalue())
  178.  
  179.         index += 20
  180.  
  181.         if first_packet:
  182.             num_questions = 0
  183.             first_packet = False
  184.  
  185.     return packets
  186.  
  187.  
  188.  
  189. def detectNetworkDevices(ttl=4, timeout=10): 
  190.     mcast_addr, mcast_port ='224.0.0.251', 5353
  191.     found_devices = {}
  192.     answers = []
  193.  
  194.     s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
  195.  
  196.     x = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  197.     x.connect(('1.2.3.4', 56))
  198.     intf = x.getsockname()[0]
  199.     x.close()
  200.  
  201.     s.setblocking(0)
  202.     ttl = struct.pack('B', ttl) 
  203.  
  204.     try:
  205.         s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
  206.         s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
  207.     except (AttributeError, socket.error):
  208.         pass
  209.  
  210.     try:
  211.         s.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, ttl)
  212.         s.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(intf) + socket.inet_aton('0.0.0.0'))
  213.         s.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP ,1)
  214.     except Exception, e:
  215.         log.error("Unable to setup multicast socket for mDNS: %s" % e)
  216.         return {}
  217.  
  218.     now = time.time()
  219.     next = now
  220.     last = now + timeout
  221.     delay = 1
  222.  
  223.     while True:
  224.         now = time.time()
  225.  
  226.         if now > last:
  227.             break
  228.  
  229.         if now >= next:
  230.             try:
  231.                 for p in create_outgoing_packets(answers):
  232.                     log.debug("Outgoing: (%d)" % len(p))
  233.                     log.log_data(p, width=16)
  234.                     s.sendto(p, 0, (mcast_addr, mcast_port))
  235.  
  236.             except socket.error, e:
  237.                 log.error("Unable to send broadcast DNS packet: %s" % e)
  238.  
  239.             next += delay
  240.             delay *= 2
  241.  
  242.         update_spinner()
  243.  
  244.         r, w, e = select.select([s], [], [s], 0.5)
  245.  
  246.         if not r: 
  247.             continue
  248.  
  249.         data, addr = s.recvfrom(16384)
  250.  
  251.         if data:
  252.             update_spinner()
  253.             y = {'num_devices' : 1, 'num_ports': 1, 'product_id' : '', 'mac': '', 
  254.                  'status_code': 0, 'device2': '0', 'device3': '0', 'note': ''}
  255.  
  256.             log.debug("Incoming: (%d)" % len(data))
  257.             log.log_data(data, width=16)
  258.  
  259.             offset = 0
  260.             offset, (id, flags, num_questions, num_answers, num_authorities, num_additionals) = \
  261.                 read_data_unpack(offset, data, "!HHHHHH")
  262.  
  263.             log.debug("Response: ID=%d FLAGS=0x%x Q=%d A=%d AUTH=%d ADD=%d" % (id, flags, num_questions, num_answers, num_authorities, num_additionals))
  264.  
  265.             for question in range(num_questions):
  266.                 update_spinner()
  267.                 offset, name = read_name(offset, data)
  268.                 offset, (typ, cls) = read_data_unpack(offset, data, "!HH")
  269.                 log.debug("Q: %s TYPE=%d CLASS=%d" % (name, typ, cls))
  270.  
  271.             fmt = '!HHiH'
  272.             for record in range(num_answers + num_authorities + num_additionals):
  273.                 update_spinner()
  274.                 offset, name = read_name(offset, data)
  275.                 offset, info = read_data_unpack(offset, data, "!HHiH")
  276.  
  277.                 if info[0] == QTYPE_A: # ipv4 address
  278.                     offset, result = read_data(offset, data, 4)
  279.                     ip = '.'.join([str(ord(x)) for x in result])
  280.                     log.debug("A: %s" % ip)
  281.                     y['ip'] = ip
  282.  
  283.                 elif info[0] == QTYPE_PTR: # PTR
  284.                     offset, name = read_name(offset, data)
  285.                     log.debug("PTR: %s" % name)
  286.                     y['mdns'] = name
  287.                     answers.append(name.replace("._pdl-datastream._tcp.local.", ""))
  288.  
  289.                 elif info[0] == QTYPE_TXT: 
  290.                     offset, name = read_data(offset, data, info[3])
  291.                     txt, off = {}, 0
  292.  
  293.                     while off < len(name):
  294.                         l = ord(name[off])
  295.                         off += 1
  296.                         result = name[off:off+l]
  297.  
  298.                         try:
  299.                             key, value = result.split('=')
  300.                             txt[key] = value
  301.                         except ValueError:
  302.                             pas
  303.  
  304.                         off += l
  305.  
  306.                     log.debug("TXT: %s" % repr(txt))
  307.                     y['device1'] = "MFG:Hewlett-Packard;MDL:%s;CLS:PRINTER;" % txt['ty']
  308.  
  309.                     if 'note' in txt:
  310.                         y['note'] = txt['note']
  311.  
  312.                 elif info[0] == QTYPE_SRV: 
  313.                     offset, (priority, weight, port) = read_data_unpack(offset, data, "!HHH")
  314.                     ttl = info[3]
  315.                     offset, server = read_name(offset, data)
  316.                     log.debug("SRV: %s TTL=%d PRI=%d WT=%d PORT=%d" % (server, ttl, priority, weight, port))
  317.                     y['hn'] = server.replace('.local.', '')
  318.  
  319.                 elif info[0] == QTYPE_AAAA: # ipv6 address
  320.                     offset, result = read_data(offset, data, 16)
  321.                     log.debug("AAAA: %s" % repr(result))
  322.  
  323.                 else:
  324.                     log.error("Unknown DNS record type (%d)." % info[0])
  325.                     break
  326.  
  327.         found_devices[y['ip']] = y
  328.  
  329.  
  330.     log.debug("Found %d devices" % len(found_devices))
  331.  
  332.     return found_devices
  333.  
  334.  
  335.